# coding: utf-8
import codecs
from hashlib import blake2b as hash_
import time
from reedsolo import RSCodec ,ReedSolomonError
from tqdm import tqdm
import base64
import wx   #wxpython
import wx.xrc
import wx.grid as grid
import math
import os
import sys
import sqlite3
import logging
from threading import Thread
from pubsub import pub #pypubsub
from hash_gui import *
# from copy_gui import *
# import copy
import zipfile

#import pyautogui

#pip install pypubsub wxpython tqdm reedsolo pandas
########################################################################
class MF1(MyFrame1):
    def __init__(self, parent):
        super().__init__(parent)
        self.Centre(wx.BOTH)
        self.Bind(wx.EVT_CLOSE, self.onExit)
    def onExit(self, event):

        dlg = wx.MessageDialog(None, u"确定退出？", u"确定退出",
                               wx.YES_NO | wx.ICON_QUESTION)
        if dlg.ShowModal() == wx.ID_YES:
            if(db != None):
                db.close()
            sys.exit(0)
        dlg.Destroy()
########################################################################
class MF2(MyFrame2):
    def __init__(self, parent):
        super().__init__(parent)
    def MyFrame2OnClose(self, event):
        event.Skip()

    def inputFile(self, event):
        event.Skip()

    def outputFile(self, event):   
        event.Skip()

    def onReset(self, event):
        event.Skip()

    def refreshList(self, event):
        event.Skip()

    def updateFileHash(self, event):
        event.Skip()

    def delFilelist(self, event):
        event.Skip()


########################################################################
class MP1(MyPanel1):
    def __init__(self, parent):
        super().__init__(parent)
        pub.subscribe(self.updateDisplay, "update")
        pub.subscribe(self.up, "up")
        # pub.subscribe(self.chargeButon,"")
    def __del__(self):
        pass

    def up(self, msg):
        self.m_staticText1.SetLabel("目录总计：%s   文件总计：%s  已完成：%s 时间：%2.2f" % (
            msg['dircount'], msg["filecount"], msg['complete'], time.time()-nowtime))
        w.m_statusBar1.SetStatusText("%s" % msg["path"], 0)
        if(msg['filecount'] == 0):
            t = 0
        else:
            t = math.ceil(msg['complete']/msg['filecount']*100)
        self.m_gauge5.SetValue(t)
        if (msg["ThreadRun"]):
            self.m_button1.Disable()
            self.m_button3.Disable()
        else:
            self.m_button1.Enable()
            self.m_button3.Enable()

    def updateDisplay(self, msg):
        t = msg
        self.m_staticText1.SetLabel("目录总计：%s   文件总计：%s  已完成：%s 时间：%2.2f" % (
            info['dircount'], info["filecount"], info['complete'], time.time()-nowtime))
        if isinstance(t, int):  # 如果是数字，说明线程正在执行，显示数字
            # self.m_staticText2.SetLabel("%s%%" % t)
            self.m_gauge6.SetValue(t)
        else:  # 否则线程未执行，将按钮重新开启
            # self.m_staticText2.SetLabel("%s" % t)
            self.m_button1.Enable()
        self.m_staticText1.SetLabel("目录总计：%s   文件总计：%s  已完成：%s 时间：%2.2f" % (
            info['dircount'], info["filecount"], info['complete'], time.time()-nowtime))
    # Virtual event handlers, overide them in your derived class

    def onDirC(self, event):
        global info
        global db
        info['root'] = event.GetPath()
        print(info['root'])
        db = sqlite3.connect(
            info['root']+"/"+logfilename+".db", check_same_thread=False)
        #db = sqlite3.connect(":memory:", check_same_thread=False)
        cur = db.cursor()
        result = cur.execute(
            "select * from sqlite_master where type = 'table' and name = 'list'")
        row = result.fetchall()
        print(len(row))
        if(len(row) > 0):
            r = msg("文件列表已存在，是否更新文件列表？", "更新文件列表",
                    wx.YES_NO | wx.ICON_INFORMATION)
            if(r == wx.ID_YES):

                trySql('DROP table IF EXISTS list')
                trySql('DROP table IF EXISTS dirs')
                trySql(
                    'create table list (path varchar(256) NOT NULL primary key, hash varchar(2),  checks varchar(2))')
                trySql(
                    'create table dirs (path varchar(256) NOT NULL primary key, hash varchar(2),  checks varchar(2))')

            else:
                result = cur.execute("select count(path) from dirs")  # 清空文件夹列表
                row = result.fetchone()
                info['dircount'] = row[0]
                result = cur.execute("select count(path) from list")  # 清空文件列表
                row = result.fetchone()
                info['filecount'] = row[0]
                self.updateDisplay(info)
                return True

        trySql('create table list (path varchar(256) NOT NULL primary key, hash varchar(2),  checks varchar(2))')
        trySql('create table dirs (path varchar(256) NOT NULL primary key, hash varchar(2),  checks varchar(2))')
        trySql("create table hash (path varchar(256) primary key, hash varchar(128))")
        trySql("create table checks (path varchar(256) primary key, hash varchar(128))")
        trySql("CREATE INDEX index_hash ON hash (hash);")

        trySql("DROP VIEW IF EXISTS \"repeat\";")
        trySql("CREATE VIEW \"main\".\"repeat\" AS SELECT hash.path,hash.hash FROM hash WHERE hash.hash IN (select hash.hash from hash GROUP BY hash.hash having count(hash.hash)>1) ORDER BY hash.hash DESC;")
        trySql("DROP VIEW IF EXISTS \"check\";")
        trySql("CREATE VIEW \"main\".\"check\" AS SELECT checks.path,hash.hash AS hash ,checks.hash AS checks  FROM checks LEFT JOIN hash ON checks.path = hash.path WHERE checks.hash <> hash.hash OR checks.hash IS NULL OR hash.hash IS NULL;")
        trySql("DROP VIEW IF EXISTS \"hashlist\";")
        trySql("CREATE VIEW \"main\".\"hashlist\" AS SELECT list.path , hash.hash FROM list LEFT JOIN hash ON hash.path = list.path WHERE hash.hash IS NULL OR hash.path IS NULL OR hash.hash = \"fail!\" ;")
        trySql("DROP VIEW IF EXISTS \"checkslist\";")
        trySql("CREATE VIEW \"main\".\"checkslist\" AS SELECT list.path , checks.hash FROM list LEFT JOIN checks ON checks.path = list.path WHERE checks.hash IS NULL OR checks.path IS NULL OR checks.hash = \"fail!\" ;")
        trySql("DROP VIEW IF EXISTS \"alllist\";")
        trySql("CREATE VIEW \"alllist\" AS SELECT list.path, hash.hash, checks.hash AS checks FROM list LEFT JOIN hash ON list.path = hash.path LEFT JOIN checks ON checks.path = list.path ;")
        trySql("DROP VIEW IF EXISTS \"main\".\"lostfile\" ;")
        trySql("CREATE VIEW \"main\".\"lostfile\" AS SELECT hash.path, hash.hash FROM hash LEFT JOIN list ON list.path = hash.path WHERE list.hash is NULL ;")

        ScanThread()
        # CheckFile()
        event.Skip()

    def onStart(self, event):
        self.m_button2.SetLabel("暂停")
        info['check'] = False
        HashThread()
        event.Skip()

    def onExit(self, event):
        if(info["ThreadRun"] == True):
            info["ThreadRun"] = False
            self.m_button2.SetLabel("退出")
        else:
            dlg = wx.MessageDialog(
                None, u"确定退出？", u"确定退出", wx.YES_NO | wx.ICON_QUESTION)
            if dlg.ShowModal() == wx.ID_YES:
                if(db != None):
                    db.close()
                sys.exit(0)
            dlg.Destroy()

        event.Skip()

    def onInfo(self, event):
        global w2
        global p2_
        global db
        if(db == None):
            dlg = wx.MessageDialog(None, u"请选择目录", '',
                                   wx.ICON_WARNING)
            dlg.ShowModal()
            dlg.Destroy()
            return False
        if(w2 == None):
            try:
                w2 = f2(None)
                p2_=p2(w2)
                w2.Show()
            except BaseException as e:
                print(e)
                w2 = None
        else:
            print(w2)

    def onVerify(self, event):

        self.m_button2.SetLabel("暂停")
        info['check'] = True
        HashThread()
        event.Skip()
        CheckFile()
        cur = db.cursor()
        r = cur.execute("select count(path) from list where checks=1")
        n = r.fetchone()
        logging.debug("complete:%d" % n[0])
        event.Skip()

########################################################################
class f2(MF2):
    def MyFrame2OnClose(self, event):
        global w2
        w2.Destroy()
        w2 = None
        event.Skip()
    def inputFile(self, event):
        event.Skip()
    def reFile( self, event ):
        dlg = wx.DirDialog(self, "备份文件夹",style=wx.DD_DEFAULT_STYLE)
        dlg.ShowModal()
        global w2
        w2.Destroy()
        w2=f2(None)
        p3(w2,dlg.GetPath())
        w2.Title="文件恢复"
        w2.Show()
        dlg.Destroy()
        event.Skip()
    def outputFile(self, event):
        global p2_
        global db
        cur=db.cursor()
        result = cur.execute("select count(*) from hash ")
        row = result.fetchone()
        total = row[0]
        lens=int(total/BackupFileSize)+1
        dlg = wx.ProgressDialog("导出校验记录", " ", maximum = total,parent=None,style = 0| wx.PD_APP_MODAL | wx.PD_ESTIMATED_TIME| wx.PD_REMAINING_TIME )
        dlgn=0
        with tqdm(total=total) as bar:
            with zipfile.ZipFile(f"{info['root']}\\{logfilename[:-4]}bak.zip", 'w',allowZip64=True) as myzip:  #compression=zipfile.ZIP_LZMA,
                myzip.writestr(f"config.txt",f"{row[0]}")
                for i in range(lens):
                    sql_str=""
                    result = cur.execute(f"select * from hash LIMIT {BackupFileSize*i}, {BackupFileSize}") 
                    row=result.fetchall()
                    for n in row:
                        bar.update(1)
                        dlg.Update(dlgn,f"{dlgn}/{total}")
                        dlgn=dlgn+1
                        try:
                            text1=eccBase64En(n)
                            sql_str=sql_str+f"{n[0]}\t{text1}\n"
                        except:
                            logging.error(n[0])
                    myzip.writestr(f"{i}.log",sql_str)
        dlg.Destroy()
        msg("已成功导出hash")
        event.Skip()

    def onReset(self, event):
        if(msg(u"确定清空所有校验记录？", u"重置校验", wx.YES_NO | wx.ICON_INFORMATION) == wx.ID_YES):
            if(trySql('DELETE FROM checks')):
                msg(u"已清空所有校验记录", u"成功")
            else:
                msg(u"清除失败", u"错误", wx.ICON_ERROR)

    def refreshList(self, event):
        if(msg(u"确定重新扫描目录？", u"更新目录", wx.YES_NO | wx.ICON_INFORMATION) == wx.ID_YES):
            trySql('DELETE FROM list')
            trySql('DELETE FROM dirs')
            ScanThread()
            event.Skip()

    def updateFileHash(self, event):
        if(msg(u"确定更新校验记录？", u"更新记录", wx.YES_NO | wx.ICON_INFORMATION) == wx.ID_YES):
            if(trySql('update hash set hash=(select checks from `check` where path = hash.path) where path in (select path from `check`)')):
                msg(u"更新成功！")
            event.Skip()

    def delFilelist(self, event):
        if(msg(u"确定删除缺失记录？", u"删除文件", wx.YES_NO | wx.ICON_INFORMATION) == wx.ID_YES):
            if(trySql('DELETE FROM `checks` WHERE path in (select path from `lostfile`)') and
                    trySql('DELETE FROM `hash` WHERE path in (select path from `lostfile`)')):
                msg(u"删除成功！")
            event.Skip()

    def delCheckList(self,event):
        if(msg(u"确定删除差异文件校验记录？", u"重置校验", wx.YES_NO | wx.ICON_INFORMATION) == wx.ID_YES):
            if(trySql('DELETE FROM `checks` WHERE path in (select path from `check`)')):
                msg(u"清除成功！")
        event.Skip()
########################################################################
class p2(MyPanel2):
    def __init__(self, parent):
        super().__init__(parent)
        self.table = "alllist"
        global db
        self.db = db
        self.cur = db.cursor()
        self.getPageCount()
        self.showpage(1)

    def onUpPage(self, event):
        page = self.m_comboBox3.GetStringSelection()
        self.showpage(int(page)-1)
        event.Skip()

    def OnChosePage(self, event):
        self.showpage(int(self.m_comboBox3.GetValue()))
        event.Skip()

    def OnGotoPage(self, event):
        if(self.m_comboBox3.GetValue().isdigit()):
            if(int(self.m_comboBox3.GetValue()) < 1 or int(self.m_comboBox3.GetValue()) > self.pageCount):
                dlg = wx.MessageDialog(None, u"超出页码范围", '', wx.ICON_WARNING)
                dlg.ShowModal()
                dlg.Destroy()
                self.m_comboBox3.SetSelection(0)
                return False
        else:
            dlg = wx.MessageDialog(None, u"请输入有效数字", '', wx.ICON_WARNING)
            dlg.ShowModal()
            dlg.Destroy()
            self.m_comboBox3.SetSelection(0)
            return False
        self.showpage(int(self.m_comboBox3.GetValue()))
        event.Skip()

    def OnDownPage(self, event):
        page = self.m_comboBox3.GetValue()
        self.showpage(int(page)+1)
        event.Skip()

    def OnShowAll(self, event):
        w2.SetTitle(u"查看记录")
        self.table = "alllist"
        self.getPageCount()
        self.showpage(1)
        event.Skip()

    def OnShowAddfile(self, event):
        w2.SetTitle(u"新增文件")
        self.table = "hashlist"
        self.getPageCount()
        self.showpage(1)
        event.Skip()

    def OnShowLostfile(self, event):
        w2.SetTitle(u"丢失文件")
        self.table = "lostfile"
        self.getPageCount()
        self.showpage(1)
        event.Skip()

    def onReset(self, event):
        dlg = wx.MessageDialog(None, u"确定清空所有校验记录？",
                               u"重置校验", wx.YES_NO | wx.ICON_QUESTION)
        if dlg.ShowModal() == wx.ID_YES:
            try:
                self.db.execute('DELETE FROM checks')
            except (sqlite3.Error, Exception) as e:
                logging.error("Database error: %s" % e)
                self.db.rollback()
            else:
                self.db.commit()
        dlg.Destroy()

    def OnShowRepeat(self, event):
        w2.SetTitle(u"重复文件")
        self.table = "repeat"
        self.getPageCount()
        self.showpage(1)
        event.Skip()

    def m_button6OnButtonClick(self, event):
        w2.SetTitle(u"差异文件")
        self.table = "check"
        self.getPageCount()
        self.showpage(1)
        event.Skip()

    def showpage(self, index):
        if(self.fileCount == 0):
            # print(msg("没有记录"))
            #self.table = "alllist"
            gridDatas = [(u"无记录", u"无记录", u"无记录")]
        else:
            sql = "select * from `%s` LIMIT %d, 1000" % (
                self.table, (index-1)*1000)
            logging.debug(sql)
            result = self.cur.execute(sql)
            logging.debug(sql)
            gridDatas = result.fetchall()
        # self.gridTable = wx.grid.Grid(self.panel, -1, pos=(5, 5), size=(490, 300), style=wx.WANTS_CHARS)
        self.infoTable = MyDataTable(gridDatas, [u"路径", u"校验记录", u"生成校验"])
        self.m_grid1.SetTable(self.infoTable, True)
        self.m_grid1.SetColSize(0, 500)
        self.m_grid1.SetColSize(1, 280)
        try:
            self.m_grid1.SetColSize(2, 280)
        except:
            pass
        self.m_grid1.SetRowLabelSize(50)
        # self.m_grid1.AutoSize()
        self.m_grid1.Refresh()
        # self.m_comboBox3.SetValue(str(index))
        self.m_comboBox3.SetSelection(index-1)
        if(self.pageCount == 1):
            self.m_button11.Disable()
            self.m_button12.Disable()
        else:
            if(index == 1):
                self.m_button11.Disable()
                self.m_button12.Enable()
            if(index >= self.pageCount):
                self.m_button12.Disable()
                self.m_button11.Enable()
            if(index > 1 and index < self.pageCount):
                self.m_button11.Enable()
                self.m_button12.Enable()

    def onExit(self, event):
        global w2
        w2.Destroy()
        w2 = None
        event.Skip()

    def getPageCount(self):
        global w2
        r = self.cur.execute("select count(*) from `%s`" % (self.table))
        rows = r.fetchall()
        self.fileCount = rows[0][0]
        self.pageCount = int(self.fileCount/1000)+1
        w2.m_statusBar2.SetStatusText(u"文件总计：%d" % (self.fileCount))
        self.m_comboBox3.Clear()
        for i in range(self.pageCount):
            self.m_comboBox3.Append(str(i+1))
        self.m_comboBox3.SetSelection(0)
    def OnLDC(self, event):
        os.startfile(os.path.join(info['root'], self.infoTable.data[self.m_grid1.GridCursorRow][0].lstrip("\\")))
        event.Skip()

########################################################################
class p3(MyPanel7):
    def __init__(self, parent,path):
        super().__init__(parent)    
        global db
        self.remotePath=path
        self.localDb=db
        self.size=10
        self.cur=db.cursor()
        result=self.cur.execute("select `path`,`hash` from 'check' LIMIT 0, 1000")
        result=result.fetchall()
        if(len(result)==0):
            gridDatas = [(u"无记录", u"无记录", u"无记录", u"无记录", u"无记录")]
        else:
            gridDatas=result
        self.infoTable = MyDataTable(gridDatas, [u"路径", u"本地记录", u"本地校验", u"备份记录", u"备份校验"])
        self.m_grid4.SetTable(self.infoTable, True)
        self.m_grid4.SetColSize(0, 500)
        self.m_grid4.SetColSize(1, 280)
        self.m_grid4.SetRowLabelSize(50)
        # self.m_grid4.AutoSize()
        self.m_grid4.Refresh()
    def __del__(self):
        pass
    def recoverFilesAll( self, event ):
        msg(self.remotePath)
        event.Skip()


    def recoverFilesSelect( self, event ):
        event.Skip()
    def checkRemoteFile(self, event):
        trySql("DROP TABLE IF EXISTS \"remotehash\";")
        trySql("create table remotehash (path varchar(256) primary key, hash varchar(128))")
        result=self.cur.execute(f"select `path`,`hash` from 'check' LIMIT 0, {self.size}")
        result=result.fetchall()
        n=1
        while(len(result)>0):
            for i in result:
                logging.info(i)
                HashThread.getHash(self.self.remotePath+i[0])
            result=self.cur.execute(f"select `path`,`hash` from 'check' LIMIT {n*self.size}, {self.size}")
            result=result.fetchall()
            n=n+1
        event.Skip()
    def back( self, event ):
        global w2
        w2.Destroy()
        w2=f2(None)
        p2(w2)
        w2.Title="查看记录"
        w2.Show()

        event.Skip()

########################################################################
class ScanThread(Thread):
    def __init__(self):
        Thread.__init__(self)
        global nowtime
        nowtime = time.time()
        self.start()

    def run(self):
        global db
        cur = db.cursor()
        '''
        result = cur.execute("SELECT * FROM sqlite_master WHERE name = 'list'")
        row = result.fetchall()
        s = len(row)
        if(s > 0):
            
            dlg = wx.MessageDialog(
                None, u"文件列表已存在，是否使用？", u"文件列表", wx.YES_NO | wx.ICON_QUESTION)
            if dlg.ShowModal() == wx.ID_YES:
                dlg.Destroy()
                result = cur.execute(
                    "SELECT count(path) FROM list WHERE hash='1'")
                row = result.fetchone()
                info['complete'] = row[0]
                result = cur.execute("SELECT count(path) FROM list")
                row = result.fetchone()
                info['filecount'] = row[0]
                info['dircount'] = 0
                wx.CallAfter(pub.sendMessage, "up", msg=info)
                return False
            '''

        info["ThreadRun"] = True
        wx.CallAfter(pub.sendMessage, "up", msg=info)
        self.path = info['root']
        p.m_textCtrl1.AppendText("扫描目录："+self.path+"\n")
        self.scan()
        info["ThreadRun"] = False
        wx.CallAfter(pub.sendMessage, "up", msg=info)

    def scan(self):
        info['filelist'] = []
        info['filecount'] = 0
        info['dircount'] = 0
        info['complete'] = 0
        for dir_path, subpaths, files in os.walk(self.path, True):
            try:
                db.execute("INSERT INTO dirs (path,hash,checks) VALUES ('%s', '%s', '%s' )" % ((dir_path[len(
                    info['root'].rstrip("\\")):].replace("\'", "\'\'") if len(dir_path) != len(info['root']) else '\\'), '0', '0'))
            except (sqlite3.Error, Exception) as e:
                logging.info(dir_path)
                logging.error("Database dirs insert error: %s" % e)
                p.m_textCtrl1.AppendText("error： %s \n" % dir_path)
                # self.db.rollback()
            for file in files:
                # if(info["ThreadRun"] == False):
                #     print("exit")
                #     return False
                # if file == logfilename or file == logfilename+".db":  ###############################################
                #     continue
                file_path = os.path.join(dir_path, file)
                info["file"] = file
                info["path"] = dir_path
                # info['filelist'].append(file_path)
                # logging.debug(file_path)
                try:
                    db.execute("INSERT INTO list (path,hash,checks) VALUES ('%s', '%s', '%s' )" % (
                        file_path[len(info['root'].rstrip("\\")):].replace("\'", "\'\'"), '0', '0'))
                except (sqlite3.Error, Exception) as e:
                    logging.info(file_path)
                    logging.error("Database error: %s" % e)
                    p.m_textCtrl1.AppendText("error： %s \n" % file_path)
                    # self.db.rollback()
                info['filecount'] += 1
            info['dircount'] += 1
            wx.CallAfter(pub.sendMessage, "up", msg=info)
        db.commit()
        cur = db.cursor()
        # trySql()
        r = cur.execute("select count(path) from dirs")  #
        c = r.fetchone()
        info['dircount'] = c[0]
        r = cur.execute("select count(path) from list")  #
        c = r.fetchone()
        info['filecount'] = c[0]


class HashThread(Thread):
    def __init__(self):
        Thread.__init__(self)
        global nowtime
        nowtime = time.time()
        self.start()

    def run(self):
        global db

        logging.debug(db)
        logging.debug(info['check'])
        dbname = "checks" if info['check'] else 'hash'
        fs = 'checks'if info['check'] else 'hash'
        info["ThreadRun"] = True
        logfile = os.path.join(info['root'], logfilename)
        print(logfile)
        log = codecs.open(logfile, "w+", "utf-8")
        cur = db.cursor()
        r = cur.execute("select path from %s where hash='fail!'" %
                        dbname)  # 清除未完成任务
        for c in r.fetchall():
            logging.info(c[0])
            try:
                db.execute("UPDATE list SET %s = '0' WHERE path = '%s'" %
                            (fs, c[0].replace("\'", "\'\'")))
            except (sqlite3.Error, Exception) as e:
                logging.error("Database UPDATE error: %s" % e)
        try:
            db.execute("DELETE FROM %s  WHERE hash = 'fail!'" % (dbname))
        except (sqlite3.Error, Exception) as e:
            logging.error("Database UPDATE error: %s" % e)
        db.commit()
        r = cur.execute("select count(path) from %s" % fs)  # 完成任务量
        n = r.fetchone()
        logging.debug("complete:%d" % n[0])
        n = n[0]
        r = cur.execute("select path from %slist" % fs)
        p.m_textCtrl1.AppendText("%s 开始\n" % (
            time.strftime('%Y-%m-%d %H:%M:%S')))
        for c in r.fetchall():
            logging.debug(c[0])
            f = os.path.join(info['root'], c[0].strip("\\"))
            if(info["ThreadRun"] == False):
                db.commit()
                p.m_textCtrl1.AppendText("%s 已暂停\n" % (
                    time.strftime('%Y-%m-%d %H:%M:%S')))
                '''
                info["ThreadRun"]=False
                wx.CallAfter(pub.sendMessage, "up", msg=info)
                return None
                '''
                break
            info['complete'] = n
            info['path'] = f
            wx.CallAfter(pub.sendMessage, "up", msg=info)
            if(not (os.path.basename(f) == logfilename or
                    os.path.basename(f) == logfilename+".asc" or
                    os.path.basename(f) == logfilename+".db" or
                    not os.path.isfile(f))):
                hash = self.getHash(f)
                # if(not(hash=="fail!")):
                log.write(u"%s,%s,%s \n" % (time.strftime(
                    '%Y-%m-%d %H:%M:%S'), hash, RelativePath(f)))
                self.insertdb(RelativePath(f), hash, dbname, fs)
            n = n+1
        log.close()
        cur.close()
        db.commit()
        info["ThreadRun"] = False
        info['complete'] = n
        # info['complete']=info['filecount']
        p.m_textCtrl1.AppendText("%s 已完成%d\n" % (
            time.strftime('%Y-%m-%d %H:%M:%S'), n-1))
        p.m_button2.SetLabel("退出")
        wx.CallAfter(pub.sendMessage, "up", msg=info)
        return None

    def getHash(self, filepath):
        if(os.path.isfile(filepath)):
            size = os.path.getsize(filepath)
            # print(file,size)
            try:
                file = open(filepath, "rb")
            except:
                logging.error(f"can't open file!\t{filepath}")
                p.m_textCtrl1.AppendText(u"%s 文件%s无法打开\n" % (
                    time.strftime('%Y-%m-%d %H:%M:%S'), filepath))
                return "fail!"
            hash = hash_()
            n = 0
            bs = 1024*1024*32
            ab = math.ceil(size/bs)
            while 1:
                # 处理该行的代码
                data = file.read(bs)
                if(len(data) == 0):
                    break
                if(info["ThreadRun"] == False):
                    print("exit")
                    return "fail!"
                    db.commit()
                    break
                hash.update(data)
                n = n+1
                if(ab > 2):
                    wx.CallAfter(pub.sendMessage, "update", msg=int(n/ab*100))
                elif ab == n:
                    wx.CallAfter(pub.sendMessage, "update", msg=0)
            file.close()
            return hash.hexdigest().upper()
            # return hash.hexdigest()
        else:
            print("error")

    def insertdb(self, path, hash, dbname, fs):
        # global db
        try:
            logging.info("INSERT INTO %s (path,hash) VALUES ('%s', '%s' )" % (
                dbname.replace("\'", "\'\'"), path.replace("\'", "\'\'"), hash))
            db.execute("INSERT INTO %s (path,hash) VALUES ('%s', '%s' )" % (
                dbname.replace("\'", "\'\'"), path.replace("\'", "\'\'"), hash))
        except (sqlite3.Error, Exception) as e:
            logging.error("Database INSERT error: %s" % e)
            # db.rollback()


    '''
            try :
                db.execute("UPDATE list SET %s = '1' WHERE path = '%s'"%(fs.replace("\'","\'\'"),path.replace("\'","\'\'")))       
            except (sqlite3.Error,Exception) as e:
                logging.error("Database UPDATE error: %s" % e)
                # db.rollback()
    '''




'''
def getHash512():
    file="D:\hubo\FFXIV0115\FFXIV_180115_Data06.cab"
    size = os.path.getsize(file)
    file = open(file,"rb")
    hash = hashlib.hash512()
    n=0
    bs=1024*1024*32
    ab=math.ceil(size/bs)
    print(size)
    print(ab)
    while 1:
      # 处理该行的代码
        data = file.read(bs)
        if(len(data)==0):
            break
        hash.update(data)
        n=n+1
        print(int(n/ab*100))
        wx.CallAfter(pub.sendMessage, "update", msg=int(n/ab*100))
    file.close()
    p.m_textCtrl1.SetValue(hash.hexdigest())
    return hash.hexdigest()
'''
'''
def insertdb(path,hash):
    global db
    try :
        db.execute("INSERT INTO hash (path,hash) VALUES ('%s', '%s' )"%(path,hash))       
    except (sqlite3.Error,Exception) as e:
        logging.error("Database error: %s" % e)
        db.rollback()
    else :
        db.commit()
        logging.debug('insert into hash')
'''


class MyDataTable(grid.PyGridTableBase):
    def __init__(self, data, colLabels=None):
        grid.GridTableBase.__init__(self)
        self.colLabels = colLabels
        self.data = data

    def GetNumberRows(self):
        return len(self.data)

    def GetNumberCols(self):
        return len(self.data[0])

    def GetValue(self, row, col):
        if self.data[row][col] is None:
            return ' '
        else:
            return self.data[row][col]

    def SetValue(self, row, col, value):
        pass

    def GetColLabelValue(self, col):
        if self.colLabels != None:
            return self.colLabels[col]
        else:
            return "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[col]







def RelativePath(f):
    return f[len(info['root'].rstrip("\\")):]

def CheckFile():
    logfile = os.path.join(info['root'], logfilename)
    if(os.path.isfile(logfile)):
        file = codecs.open(logfile, "r", "utf-8")
        for l in file.readlines():
            logging.debug(l)
    else:
        logging.debug("not found file!")
    return False
def msg(content, title="", type=wx.ICON_INFORMATION):
    dlg = wx.MessageDialog(None, content, title, type)
    r = dlg.ShowModal()
    dlg.Destroy()
    return r
'''
wx.OK                     Show an OK button.
wx.CANCEL                 Show a Cancel button.
wx.YES_NO                 Show Yes and No buttons.
wx.YES_DEFAULT          Used with wxYES_NO, makes Yes button the default - which is the default behaviour.
wx.NO_DEFAULT             Used with wxYES_NO, makes No button the default.
wx.ICON_EXCLAMATION     Shows an exclamation mark icon.
wx.ICON_HAND             Shows an error icon.
wx.ICON_ERROR             Shows an error icon - the same as  wx.ICON_HAND.
wx.ICON_QUESTION         Shows a question mark icon.
wx.ICON_INFORMATION     Shows an information (i) icon.
wx.STAY_ON_TOP          The message box stays on top of all other window, even those of the other applications (Windows only). 

'''
def trySql(sql):
    try:
        db.execute(sql)
    except (sqlite3.Error, Exception) as e:
        logging.error("Database error: %s" % e)
        db.rollback()
        return False
    else:
        db.commit()
        return True

def rscodefactory(cursor, row):
    b64=base64.b64encode(ecc.encode(bytes.fromhex(row[1])))
    return (b64.decode(),row[0])

def eccBase64En(row):
    try:
        en=f"{base64.b64encode(ecc.encode(bytes.fromhex(row[1]))).decode()}"
    except :
        logging.error(f"RScode编码错误！")
    else:
        return en
def eccBase64De(str):
    try:
        rsdecode=ecc.decode(base64.b64decode(str))
        if(rsdecode[2] != b''):
            str=str.replace("\t","\n")
            logging.error(f"ECC 发现错误! :\n{str}\n{rsdecode[1][:-int(len/4*3)].decode('UTF-8')}\n{base64.b64encode(rsdecode[1][-int(len/4*3):]).decode()}")
    except ReedSolomonError :
        logging.error("ECC 纠错失败")
        return False
    except Exception as e:
        logging.error(f"RScode解码错误:{e}")
    else:
        d1=p[0]
        d2=rsdecode[0].hex().upper()
        d3=base64.b64encode(rsdecode[1]).decode()
        d4=f"{d1}\t{d3}"
        return (d1,d2,d3,d4)
    




if __name__ == "__main__":
    root = os.path.dirname(os.path.realpath(__file__))
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s -%(levelname)s -%(process)d -%(module)s -%(funcName)s -%(message)s')
    info = {"ThreadRun": False,
            "dircount": 0, "filecount": 0,
            "filelist": [], 'complete': 0,
            "path": "", 'root': None, 'check': False}

    nowtime = time.time()
    filelist = []
    newfilelist = []
    errorfilelist = []
    db = None
    w2 = None
    BackupFileSize=1000
    logfilename = "blake2.log"
    logging.debug(nowtime)
    ecc=RSCodec(32)#纠错码字长度，可纠正len/2个误读
    info["root"]=r"D:"
    # db = sqlite3.connect(info['root']+"/"+logfilename+".db", check_same_thread=False)
    app = wx.App()
    w = MF1(None)
    p = MP1(w)
    w.Show()
    # w2 = f2(None)
    # p3(w2,r"F:\Bakup")
    # w2.Show()
    app.MainLoop()
